#ifndef MATH_H
#define MATH_H

#include <math.h>

///////////////////////////////////////////////////////////////////////////////

struct mat4;

struct vec2
{
  vec2() {};
  vec2(float xx, float yy) { x=xx; y=yy; }

  vec2 operator+(const vec2 &rhs) const { return vec2(x+rhs.x, y+rhs.y); }
  vec2 operator-(const vec2 &rhs) const { return vec2(x-rhs.x, y-rhs.y); }

  vec2 &operator+=(const vec2 &rhs) { x+=rhs.x; y+=rhs.y; return *this; }

  float x, y;
};

struct vec3
{
  vec3() {};
  vec3(float xx, float yy, float zz) { x=xx; y=yy; z=zz; }

  vec3 operator+(const vec3 &rhs) const { return vec3(x+rhs.x, y+rhs.y, z+rhs.z); }
  vec3 operator-(const vec3 &rhs) const { return vec3(x-rhs.x, y-rhs.y, z-rhs.z); }

  vec3 operator*(float scale) const { return vec3(x*scale, y*scale, z*scale); }
  vec3 operator/(float scale) const { return (*this) * (1.0f/scale); }

  vec3 &operator+=(const vec3 &rhs) { x+=rhs.x; y+=rhs.y; z+=rhs.z; return *this; }

  float length() const { return sqrtf(x*x+y*y+z*z); }
  float sqlength() const { return x*x+y*y+z*z; }

  vec3 normalized() const { return (*this)*(1.0f/length()); }

  float x, y, z;
};

struct vec4
{
  vec4() {};
  vec4(float xx, float yy, float zz, float ww) { x=xx; y=yy; z=zz; w=ww; }

  vec4 operator*(float scale) const { return vec4(x*scale, y*scale, z*scale, w*scale); }
  vec4 operator/(float scale) const { return (*this) * (1.0f/scale); }

  float length() const { return sqrtf(x*x+y*y+z*z+w*w); }

  vec4 normalized() const { return (*this)*(1.0f/length()); }

  float x, y, z, w;
};

struct mat3
{
  mat3() {}
  explicit mat3(const mat4 &rhs);
	float m[3][3];
};

struct mat4
{
  static const mat4 identity;

  mat4() {}
  mat4(const vec3 &trans) { m[0][0]=1; m[0][1]=0; m[0][2]=0; m[0][3]=0; m[1][0]=0; m[1][1]=1; m[1][2]=0; m[1][3]=0; m[2][0]=0; m[2][1]=0; m[2][2]=1; m[2][3]=0; m[3][0]=trans.x; m[3][1]=trans.y; m[3][2]=trans.z; m[3][3]=1.0f; }
	mat4(float _e00, float _e01, float _e02, float _e03, float _e10, float _e11, float _e12, float _e13, float _e20, float _e21, float _e22, float _e23, float _e30, float _e31, float _e32, float _e33) { m[0][0]=_e00; m[0][1]=_e01; m[0][2]=_e02; m[0][3]=_e03; m[1][0]=_e10; m[1][1]=_e11; m[1][2]=_e12; m[1][3]=_e13; m[2][0]=_e20; m[2][1]=_e21; m[2][2]=_e22; m[2][3]=_e23; m[3][0]=_e30; m[3][1]=_e31; m[3][2]=_e32; m[3][3]=_e33;	}
	mat4(float _e00, float _e01, float _e02, float _e10, float _e11, float _e12, float _e20, float _e21, float _e22) { m[0][0]=_e00; m[0][1]=_e01; m[0][2]=_e02; m[0][3]=0.0f; m[1][0]=_e10; m[1][1]=_e11; m[1][2]=_e12; m[1][3]=0.0f; m[2][0]=_e20; m[2][1]=_e21; m[2][2]=_e22; m[2][3]=0.0f; m[3][0]=m[3][1]=m[3][2]=0.0f; m[3][3]=1.0f;	}

  static mat4 makePerspectiveMat(float fov, float aspect, float near, float far);
  static mat4 makeRotateMat(float x, float y, float z);

  mat4 operator*(const mat4 &rhs) const;

  vec3 operator*(const vec3 &rhs) const;
  vec4 operator*(const vec4 &rhs) const;

  mat4 inverted() const;

  vec3 translation() const { return vec3(m[3][0], m[3][1], m[3][2]); }

	float m[4][4];
};

//void MakePerspectiveMat(mat4 m, float fov, float aspect, float near, float far);
//void MultiplyMat(mat4 r, const mat4 lhs, const mat4 rhs);

///////////////////////////////////////////////////////////////////////////////

inline mat3::mat3(const mat4 &rhs)
{
  m[0][0] = rhs.m[0][0]; m[0][1] = rhs.m[0][1]; m[0][2] = rhs.m[0][2];
  m[1][0] = rhs.m[1][0]; m[1][1] = rhs.m[1][1]; m[1][2] = rhs.m[1][2];
  m[2][0] = rhs.m[2][0]; m[2][1] = rhs.m[2][1]; m[2][2] = rhs.m[2][2];
}

///////////////////////////////////////////////////////////////////////////////

#endif
